#pragma once

enum eOpcodeType
{
	OPCODE_TYPE_UNUSED,
	OPCODE_TYPE_COMMAND,
	OPCODE_TYPE_OPERATION,
	OPCODE_TYPE_UNKNOWN
};

class COpcode
{
	friend class COpcodes;

protected:
	eOpcodeType					m_bType;
	DWORD						m_dwId;
	int							m_nIndex;
	int							m_nParseFunction;
	bool						m_bIsConditional	: 1;
	bool						m_bIsNOP			: 1;

	COpcode::COpcode(DWORD id, eOpcodeType bType)
	{
		m_bType = bType;
		m_dwId = id;
		SetIsNOP(false);
		SetParseFunc(false);
		SetIsConditional(false);
	}
	COpcode::~COpcode()
	{
	}

public:
	inline int GetParseFunc() const						{return m_nParseFunction ? (m_nParseFunction - 1) : (-1);}
	inline void SetParseFunc(int id)					{m_nParseFunction = id + 1;}
	inline void SetIsConditional(bool b)				{m_bIsConditional = b;}
	inline void SetIsNOP(bool b)						{m_bIsNOP = b;}

	inline bool IsConditional() const					{return m_bIsConditional;}
	inline bool IsNOP() const							{return m_bIsNOP;}

	inline eOpcodeType GetType() const					{return m_bType;}
	inline DWORD GetId() const							{return m_dwId;}

	template<class T> inline T * Get()					{return nullptr;}
	template<class T> inline const T * Get() const		{return nullptr;}
	template<> inline CCommand * Get<CCommand>()
	{
		return m_bType == OPCODE_TYPE_COMMAND || m_bType == OPCODE_TYPE_UNKNOWN ? reinterpret_cast<CCommand*>(this) : nullptr;
	}
	template<> inline COperation * Get<COperation>()
	{
		return m_bType == OPCODE_TYPE_OPERATION || m_bType == OPCODE_TYPE_UNKNOWN ? reinterpret_cast<COperation*>(this) : nullptr;
	}
	template<> inline const CCommand * Get<const CCommand>() const
	{
		return m_bType == OPCODE_TYPE_COMMAND || m_bType == OPCODE_TYPE_UNKNOWN ? reinterpret_cast<const CCommand*>(this) : nullptr;
	}
	template<> inline const COperation * Get<const COperation>() const
	{
		return m_bType == OPCODE_TYPE_OPERATION || m_bType == OPCODE_TYPE_UNKNOWN ? reinterpret_cast<const COperation*>(this) : nullptr;
	}
};
class COpcodes
{
	friend class COpcode;
	static COpcode	*			s_pOpcodes[MAX_OPCODES];
	static COpcode	*			s_pParseOpcodes[SCRIPT_MAX_PARSED_OPCODES];
	static int					s_nNumOpcodes;

	COpcodes()
	{
		for(int i=0; i<SCRIPT_MAX_PARSED_OPCODES; i++)
			s_pParseOpcodes[i] = nullptr;
		for(int i=0; i<MAX_OPCODES; i++)
			s_pOpcodes[i] = nullptr;
	}

public:
	static void Register(COpcode *pOpcode)
	{
		if(!s_pOpcodes[pOpcode->GetId()])
		{
			s_pOpcodes[pOpcode->GetId()] = pOpcode;
			s_nNumOpcodes++;
		}
	}
	static void Deregister(const COpcode *pOpcode)
	{
		if(s_pOpcodes[pOpcode->GetId()])
		{
			s_pOpcodes[pOpcode->GetId()] = nullptr;
			s_nNumOpcodes--;
		}
	}
	static inline void RegisterForParsing(int nId, COpcode *pOpcode)
	{
		if(!s_pParseOpcodes[nId]) s_pParseOpcodes[nId] = pOpcode;
	}
	/*static inline void DeregisterForParsing(int nId)
	{
		if(s_pParseOpcodes[nId]) s_pParseOpcodes[nId] = nullptr;
	}*/

	static inline COpcode * Get(tOpcode opcode)
	{
		return s_pOpcodes[opcode];
	}
	static inline COpcode * GetForParsing(int nId)
	{
		return nId < SCRIPT_MAX_PARSED_OPCODES ? s_pParseOpcodes[nId] : nullptr;
	}
};